package lv.senan.clients.manatee;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Contains functions for retrieving data from Manatee system.
 * 
 * @author Igors Gulbinskis
 */
public class ManateeClient {
	
	private static Logger logger = Logger.getLogger("lv.senan.clients.manatee.ManateeClient");
	
	private final int ERROR_STATUS = 11;
	
	private Socket         socket;		
	private PrintWriter    out;
	private BufferedReader in;
	
	private ConnectionSettings settings;
	
	public ManateeClient(ConnectionSettings settings) {
		this.settings = settings;
		
		socket = null;
		out    = null;
		in     = null;
	}
	
	private void hold() {
		try {
			while (!in.ready());
		} catch (IOException e) {		
			e.printStackTrace();
		}
	}
	
	private void send(String request) {
		out.println(request);
		hold();
	}
	
	private void readLastBytes() throws IOException {
		in.read(); in.read();
	}
	
	public void connect() throws UnknownHostException, IOException {
		logger.log(Level.FINEST, "client connected to Manatee server");
		
		socket = new Socket(settings.getAddress(), settings.getPort());		
		out    = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), Charset.forName("UTF-8")), true);
		in     = new BufferedReader(new InputStreamReader(socket.getInputStream(), Charset.forName("UTF-8")));
		
		send(RequestProducer.produceConnectionRequest(settings.getUsername(), settings.getPassword()));
		logger.log(Level.FINEST, "response = " + in.readLine()); readLastBytes();
	}
	
	public int getResultsCount(String query) throws IOException {
		send(RequestProducer.produceSetQueryRequest(settings.getCorpus(), query)); readLastBytes();
				
		send(RequestProducer.producePendQueryRequest());
		int status = in.read();		
		logger.log(Level.FINEST, "status = " + status); readLastBytes();
		if (status == ERROR_STATUS) {
			throw new IOException("Bad response status (" + status + ")");
		}
		
		send(RequestProducer.produceGetQueryResultCountRequest());		
		int length = Integer.parseInt(in.readLine().split(" ")[0]);
		logger.log(Level.FINEST, "length = " + length); readLastBytes();
		
		return length;
	}
	
	private String parse(String line) {
		return line.replace("\\", "")
			.replace("{}", "")
			.replaceAll("\\s+", " ")
			.trim();		
	}
	
	public String[] getResults(String query, String dimension, int startIndex, int endIndex) throws IOException {
		getResultsCount(query);
		
		send(RequestProducer.produceSetQueryCorpusRequest(settings.getCorpus()));
		logger.log(Level.FINEST, "response = " + in.readLine() + " " + in.readLine()); readLastBytes();
		
		send(RequestProducer.produceGetQueryResult(dimension, startIndex, endIndex));
		String result[] = new String[endIndex - startIndex];
		for (int i = 0; i < result.length; i++) {
			result[i] = parse(in.readLine());
		}
		return result;
	}
	
	public void disconnect() {
		if (socket != null) {
			try {
				socket.close();
			} catch (IOException e) {
				logger.warning(e.getMessage());
				e.printStackTrace();
			} finally {				
				if (in != null) {
					try {
						in.close();
					} catch (IOException e) {
						e.printStackTrace();
					} finally {
						if (out != null) {
							out.close();
						}
					}
				}
			}
		}
		logger.log(Level.FINEST, "client disconnected from Manatee server");
	}
	
}
